#pragma once

#include <queue>
#include "geometry.h"
#include "DCELHalfEdge.h"
#include "DCELFace.h"

typedef enum { SITE, CIRCLE, SITE_CIRCLE } EventType;

class VoronoiDiagram;

class Event
{
public:
	Event(EventType type)
		: type_(type), disabled_(false) {}

	EventType getType() { return type_; }
	bool isDisabled() const { return disabled_; }

	virtual double getX() const = 0;
	virtual double getY() const = 0;
	virtual void handle(VoronoiDiagram *caller) const = 0;

protected:
	EventType           type_;
	bool				disabled_;
	std::vector<size_t> beachLine_;

};

class SiteEvent : public Event
{
public:
	SiteEvent(Site site) : Event(SITE), site_(site) {}

	double getX() const { return site_.p0_.x; }
	double getY() const { return site_.p0_.y; }
	void handle(VoronoiDiagram *caller) const;

protected:
	Site site_;

};

class CircleEvent : public Event
{
public:
	CircleEvent(DCELHalfEdge *e, const Vector& c, double time)
		: Event(CIRCLE), edge_(e), prev_id(e->prev->twin->face->site_.id_),
			next_id(e->next->twin->face->site_.id_),center_(c), time_(time) {}

	double getX() const { return time_; }
	double getY() const { return center_.y; }
	void handle(VoronoiDiagram *caller) const;

protected:
	double time_;
	Vector center_;
	DCELHalfEdge *edge_;
	int prev_id;
	int next_id;
};

class EventPriorityJudge
{
public:
	bool operator()(Event* const & lhs, Event* const & rhs) const;
};

class EventQueue
{
	typedef std::vector<Event*> EventVector;
	typedef std::priority_queue<Event*, EventVector, EventPriorityJudge> Queue;
public:
	/**
	* @fn EventQueue()
	* @brief Constructor, create an empty queue.
	*/
	EventQueue() {}
	/**
	* @fn ~EventQueue()
	* @brief Destructor, delete the events remain in the queue also.
	*/
	~EventQueue();

	/**
	* @fn void init(const std::vector<common::Point> &sites)
	* @brief Initialize the queue with a set of points. Each point will rise an
	* site event.
	* @param sites a vector of input sites.
	*/
	void init(const std::vector<Site>& sites);
	/**
	* @fn void pop()
	* @brief Pop the first event.
	*/
	void pop();
	/**
	* @fn void push(Event* e)
	* @brief Add an event to the back of the event queue.
	* @param e Event to add to the queue.
	*/
	void push(Event* e) { queue_.push(e); }
	/**
	* @fn Event* top()
	* @brief Get the event on the top of the queue.
	* @return the top event.
	*/
	Event* top() { return queue_.top(); }
	/**
	* @fn bool isEmpty()
	* @brief Judge whether the queue is empty.
	* @return true if empty.
	*/
	bool isEmpty() { return queue_.empty(); }

	void clear();

private:
	Queue queue_;
};

